package org.mcflier.appuntamento.i18n;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Properties;
import java.util.ResourceBundle;

import org.mcflier.appuntamento.core.exception.PropertiesException;
import org.mcflier.appuntamento.i18n.exception.I18NCountryNotFoundException;
import org.mcflier.appuntamento.i18n.exception.I18NLanguageNotFoundException;

public class I18N {
	
	private static I18N instance;
	private static final String I18N_PROPERTIES_FILEPATH = "i18n.properties";
	private static final String I18N_LANGUAGE_TAG = "language";
	private static final String I18N_COUNTRY_TAG = "country";
	private static final String I18N_DEFAULT_LANGUAGE = "en";
	private static final String I18N_DEFAULT_COUNTRY = "US";
	
	public static final String I18N_COUNTRY_BUNDLE = "org.mcflier.appuntamento.i18n.bundles.i18n.CountryResourceBundle";
	public static final String I18N_LANGUAGE_BUNDLE = "org.mcflier.appuntamento.i18n.bundles.i18n.LanguageResourceBundle";
	
	static{
		instance = null;
	}
	
	private Properties properties;
	
	private Language language;
	private Country country;
	
	private Map<String, ResourceBundle> bundles;
	
	public static I18N getInstance() throws PropertiesException{
		if(instance == null)
			instance = new I18N();
		
		return instance;
	}
	
	private I18N() throws PropertiesException{
		this.properties = new Properties();
		this.bundles = new HashMap<String, ResourceBundle>();
		
		try {
			InputStream input = this.getClass().getResourceAsStream(I18N_PROPERTIES_FILEPATH);
			this.properties.load(input);
			input.close();
			String lang = this.properties.getProperty(I18N_LANGUAGE_TAG, I18N_DEFAULT_LANGUAGE);
			String ctry = this.properties.getProperty(I18N_COUNTRY_TAG, I18N_DEFAULT_COUNTRY);
			
			/*Temporary language and country. Improve this!!*/
			this.language = new Language(lang, "XXXX");
			this.country = new Country(ctry, "XXXX");
			/*End of crap*/
			
			this.language = new Language(lang, this.getResourceBundle(I18N_LANGUAGE_BUNDLE).getString(lang));
			this.country = new Country(ctry, this.getResourceBundle(I18N_COUNTRY_BUNDLE).getString(ctry));
		} catch (FileNotFoundException e) {
			throw new PropertiesException(e);
		} catch (IOException e) {
			throw new PropertiesException(e);
		}
	}
	
	private ResourceBundle getResourceBundle(String resourceBundle){
		if(resourceBundle == null)
			throw new NullPointerException(this.getClass().getCanonicalName()+": Resource bundle must not be null");
		if(this.bundles.containsKey(resourceBundle))
			return this.bundles.get(resourceBundle);
		else{
			ResourceBundle resource = ResourceBundle.getBundle(resourceBundle, new Locale(this.language.getCode(), this.country.getCode()));			
			this.bundles.put(resourceBundle, resource);
			return resource;
		}
	}
	
	public Language getLanguage(){
		return this.language;
	}
	
	public void setLanguage(String code) throws I18NLanguageNotFoundException, PropertiesException{
		try{
			this.language = new Language(code.trim().toLowerCase(), this.getResourceBundle(I18N_LANGUAGE_BUNDLE).getString(code.trim().toLowerCase()));
			this.properties.setProperty(I18N_LANGUAGE_TAG, code.trim().toLowerCase());
			this.storeProperties("Language changed to \""+code+"\"");
		}
		catch(NullPointerException except){
			throw new I18NLanguageNotFoundException("Language \""+code+"\" not found", except);
		}
	}
	
	public Country getCountry(){
		return this.country;
	}
	
	public void setCountry(String code) throws I18NCountryNotFoundException, PropertiesException{
		this.country = this.createCountry(code);
		this.properties.setProperty(I18N_COUNTRY_TAG, code.trim().toUpperCase());
		this.storeProperties("Country changed to \""+code+"\"");
	}
	
	public Country createCountry(String code) throws I18NCountryNotFoundException{
		try{
			Country country = new Country(code.trim().toUpperCase(), this.getResourceBundle(I18N_COUNTRY_BUNDLE).getString(code.trim().toUpperCase()));
			return country;
		}
		catch(NullPointerException except){
			throw new I18NCountryNotFoundException("Country \""+code+"\" was null", except);
		}
		catch(MissingResourceException except){
			throw new I18NCountryNotFoundException("Country \""+code+"\" not found", except);
		}
	}
	
	public Language createLanguage(String code) throws I18NLanguageNotFoundException{
		try {
			Language language = new Language(code.trim().toLowerCase(), this.getResourceBundle(I18N_LANGUAGE_BUNDLE).getString(code.trim().toLowerCase()));
			return language;
		} catch (NullPointerException e) {
			throw new I18NLanguageNotFoundException("Language \""+code+"\" was null", e);
		} catch (MissingResourceException e) {
			throw new I18NLanguageNotFoundException("Language \""+code+"\" not found", e);
		}
	}
	
	private void storeProperties(String comment) throws PropertiesException{
		try {
			FileOutputStream out = new FileOutputStream(I18N_PROPERTIES_FILEPATH);
			this.properties.store(out, (comment==null)?"--No comments--":comment);
			out.close();
		} catch (IOException e) {
			throw new PropertiesException(this.getClass().getCanonicalName()+": Error storing new properties", e);
		}
	}
}